﻿using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Ast.Core.Settings;
using Ast.Core.WinApi;
using Castle.Core.Logging;

namespace Ast.Core.Services
{
    public interface IDeviceProvider
    {
        void InstallDriver();
        void UninstallDriver();
        SafeHandle Device { get; }
    }

    public class DeviceProvider : IDeviceProvider, IDisposable
    {
        private readonly IDriverInstaller _driverInstaller;
        private readonly IDriverSettings _driverSettings;

        private bool _isDriverOwner;
        private SafeHandle _device;

        private readonly object _syncObject = new object();

        public DeviceProvider(IDriverInstaller driverInstaller, IDriverSettings driverSettings)
        {
            _driverInstaller = driverInstaller;
            _driverSettings = driverSettings;

            Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }

        public void InstallDriver()
        {
            var isInstalled = _driverInstaller.IsInstalled(_driverSettings.Name);
            if (isInstalled)
            {
                Logger.Info("uninstalling driver since it is installed already");
                _driverInstaller.Stop(_driverSettings.Name);
                _driverInstaller.Uninstall(_driverSettings.Name);
            }

            var driverPath = Path.Combine(Application.StartupPath, _driverSettings.Path);
            if (!File.Exists(driverPath))
            {
                Logger.ErrorFormat("driver not found in {0}", driverPath);
                return;
            }

            isInstalled = _driverInstaller.Install(driverPath, _driverSettings.Name, _driverSettings.DisplayName);
            if (isInstalled)
            {
                _isDriverOwner = true;
                Logger.Info("installed driver");
                
                var isStarted = _driverInstaller.Start(_driverSettings.Name);
                if (isStarted)
                {
                    Logger.Info("started driver");
                }
                else
                {
                    Logger.Win32ErrorFormat("failed to start driver {0}", driverPath);
                    _driverInstaller.Uninstall(_driverSettings.Name);
                }
            }
            else
            {
                Logger.Win32ErrorFormat("failed to install driver");
            }
        }

        public void UninstallDriver()
        {
            if (_driverInstaller.IsInstalled(_driverSettings.Name))
            {
                _driverInstaller.Stop(_driverSettings.Name);
                _driverInstaller.Uninstall(_driverSettings.Name);
                Logger.DebugFormat("uninstalled driver");
            }
        }

        public SafeHandle Device
        {
            get
            {
                if (_device == null)
                {
                    lock (_syncObject)
                    {
                        if (_device == null)
                        {
                            _device = Native.CreateFile(_driverSettings.DeviceName, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
                            if (_device.IsInvalid)
                            {
                                Logger.Win32ErrorFormat("failed to open device");
                            }
                        }
                    }
                }
                return _device;
            }
        }

        public void Dispose()
        {
            if (_device != null && !_device.IsInvalid)
            {
                Native.CloseHandle(Device.DangerousGetHandle());
                Logger.DebugFormat("closed device");
            }

            if (_isDriverOwner)
            {
                UninstallDriver();
            }
        }
    }
}